#include <cstring>
#include <iostream>               // For cout and cerr
#include <fstream>
#include <netinet/in.h>
#include "utilities.h"
#include "Blowfish.h"
#include "Packet.h"
#include "crc.h"

using namespace std;

Packet::Packet(int packSize, Blowfish* blowfish) {
	packetSize = packSize;
	maxPayloadSize = packetSize - HEADER_SIZE - PADDING_SIZE; 
	bf = blowfish;

	reset();
}

Packet::Packet(char* newPacket, int packSize, Blowfish* blowfish) {
	packetSize = packSize;
        maxPayloadSize = packetSize - HEADER_SIZE - PADDING_SIZE;
	bf = blowfish;

        reset();

	int size = 0;

	clientId = ntohl(catoi(newPacket));
	size += sizeof(clientId);
	seqNum = ntohs(catos(newPacket + size));
	size += sizeof(seqNum);
	packetNum = ntohs(catos(newPacket + size));
	size += sizeof(packetNum);
	payloadSize = ntohs(catos(newPacket + size));
        size += sizeof(payloadSize);
	eof = newPacket[size];
	size += sizeof(eof);
	crc = ntohl(catoui(newPacket + size));
        size += sizeof(crc);
        memcpy(payload, newPacket + size, maxPayloadSize + PADDING_SIZE);
}

Packet::~Packet() {
	delete[] payload;
	delete[] packet;
	//reset();
}

void Packet::reset() {
	clientId = 0;
	packetNum = 0;
	seqNum = 0;
	payloadSize = 0;
	crc = 0;
	payload = new char[maxPayloadSize + PADDING_SIZE];
	packet = new char[packetSize];
	ackRecieved = false;
	sent = false;
	errorType = NO_ERR;
	eof = '0';
	//timeout = 0L;
    memset(payload, 0, maxPayloadSize + PADDING_SIZE);
    memset(packet, 0, packetSize);
}

char* Packet::buildPacket() {
	int size = 0;

	 // Get CRC
        crcProgram.Process(payload, maxPayloadSize + PADDING_SIZE);
	crc = crcProgram.GetCRC();
	if (errorType == CORR_PACK) {
		crc++;
	}

	itoca(htons(clientId), packet);
	size += sizeof(clientId);
	stoca(htons(seqNum), packet + size);
	size += sizeof(seqNum);
	stoca(htons(packetNum), packet + size);
	size += sizeof(packetNum);
	stoca(htons(payloadSize), packet + size);
	size += sizeof(payloadSize);
	packet[size] = eof;
	size += sizeof(eof);
	uitoca(htonl(crc), packet + size);
	size += sizeof(crc);

	memcpy(packet + size, payload, maxPayloadSize + PADDING_SIZE);

	return packet;
}

char* Packet::buildAck() {
	int size = 0;

	memset(payload, 255, maxPayloadSize + PADDING_SIZE);
	payloadSize = 0; 

	itoca(htons(clientId), packet);
        size += sizeof(clientId);
        stoca(htons(seqNum), packet + size);
        size += sizeof(seqNum);
        stoca(htons(payloadSize), packet + size);
        size += sizeof(payloadSize);
        packet[size] = eof;
        size += sizeof(eof);
        uitoca(htonl(crc), packet + size);
        size += sizeof(crc);

        memcpy(packet + size, payload, maxPayloadSize + PADDING_SIZE);

        return packet;
}

void Packet::decryptPayload(string sourceAddress) {
        int encryptSize = (!(maxPayloadSize % 8 == 0) ? maxPayloadSize + 8 - (maxPayloadSize%8) : maxPayloadSize);
	bf->Decrypt(payload, encryptSize);
}

char* Packet::getPayload() {
	return payload;
}

void Packet::setPayload(char* newPayload, int ps, bool encrypt) {
	payloadSize = ps;
	memcpy(payload, newPayload, maxPayloadSize);

	if (encrypt) {
		int size = (!(maxPayloadSize % 8 == 0) ? maxPayloadSize + 8 - (maxPayloadSize%8) : maxPayloadSize);
		bf->Encrypt(payload, size);
	}
}

short Packet::getPayloadSize() {
	return payloadSize;
}

bool Packet::isEof() {
	if (eof == '0') {
		return false;
	} else {
		return true;
	}
}

void Packet::setEof(bool e) {
	if (e) {
		eof = '1';
	} else {
		eof = '0';
	}
}

bool Packet::checkCrc() {
	bool returnValue = false;

	crcProgram.Process(payload, maxPayloadSize + PADDING_SIZE);
	unsigned int newCrc = crcProgram.GetCRC();

	if (crc == newCrc) {
		returnValue = true;
	}

	return returnValue;
}

